#include <usual/string.h>

#include "test_common.h"

#define str_check(a, b) tt_str_op(a, ==, b)

#define tt_stri_op(a, op, b)                                              \
	tt_assert_test_type(a, b,#a " "#op " "#b, const char *,             \
			    (strcasecmp(_val1, _val2) op 0), "<%s>")

#define stri_check(a, b) tt_stri_op(a, ==, b)

#include <usual/crypto/hmac.h>
#include <usual/crypto/md5.h>
#include <usual/crypto/sha1.h>
#include <usual/crypto/sha256.h>
#include <usual/crypto/sha512.h>
#include <usual/crypto/sha3.h>
#include <usual/crypto/digest.h>
#include <usual/crypto/keccak_prng.h>
#include <usual/crypto/chacha.h>
#include <usual/crypto/csrandom.h>
#include <usual/cxalloc.h>

static const char *mkhex(const uint8_t *src, int len)
{
	static char buf[1024 + 1];
	static const char hextbl[] = "0123456789abcdef";
	int i;
	for (i = 0; i < len; i++) {
		buf[i*2] = hextbl[src[i] >> 4];
		buf[i*2 + 1] = hextbl[src[i] & 15];
	}
	buf[i*2] = 0;
	return buf;
}

static int hexval(char v)
{
	if (v >= '0' && v <= '9')
		return v - '0';
	if (v >= 'a' && v <= 'f')
		return v - 'a' + 10;
	if (v >= 'A' && v <= 'F')
		return v - 'A' + 10;
	return -1;
}

static uint8_t *fromhex(const char *input, int len)
{
	uint8_t *res;
	const char *s = input;
	int i, b, b1, b2;

	res = malloc(len + 1);
	if (!res)
		return NULL;

	for (i = 0; i < len; i++) {
		if (*s == '\0')
			s = input;
		b1 = hexval(*s++);
		b2 = hexval(*s++);
		b = (b1 << 4) | b2;
		if (b < 0) {
			free(res);
			return NULL;
		}
		res[i] = b;
	}

	return res;
}

static const char *run_hash(const char *str, const char *hexstr, const struct DigestInfo *impl)
{
	struct DigestContext *ctx;
	uint8_t res[512];
	uint8_t res2[512];
	int i, len, step;
	int reslen;
	uint8_t *buf = NULL;

	if (hexstr) {
		len = strlen(hexstr) / 2;
		buf = fromhex(hexstr, len);
		if (!buf)
			return "NOMEM";
		str = (char *)buf;
	} else {
		len = strlen(str);
	}

	ctx = digest_new(impl, NULL);
	if (!ctx)
		return "NOMEM";
	reslen = digest_result_len(ctx);

	digest_update(ctx, str, len);
	digest_final(ctx, res);

	digest_reset(ctx);
	step = 3;
	for (i = 0; i < len; i += step)
		digest_update(ctx, str + i,
			      (i + step <= len) ? (step) : (len - i));
	digest_final(ctx, res2);

	digest_free(ctx);

	free(buf);

	if (memcmp(res, res2, reslen) != 0)
		return "FAIL";

	return mkhex(res, reslen);
}

/*
 * MD5
 */

static const char *run_md5(const char *str)
{
	return run_hash(str, NULL, digest_MD5());
}

static void test_md5(void *ptr)
{
	str_check(run_md5(""), "d41d8cd98f00b204e9800998ecf8427e");
	str_check(run_md5("a"), "0cc175b9c0f1b6a831c399e269772661");
	str_check(run_md5("abc"), "900150983cd24fb0d6963f7d28e17f72");
	str_check(run_md5("message digest"), "f96b697d7cb7938d525a2f31aaf161d0");
	str_check(run_md5("abcdefghijklmnopqrstuvwxyz"), "c3fcd3d76192e4007dfb496cca67e13b");
	str_check(run_md5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), "d174ab98d277d9f5a5611c2c9f419d9f");
	str_check(run_md5("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), "57edf4a22be3c955ac49da2e2107b67a");
end:    ;
}

/*
 * SHA1
 */

static const char *run_sha1(const char *str)
{
	return run_hash(str, NULL, digest_SHA1());
}

static void test_sha1(void *ptr)
{
	str_check(run_sha1(""), "da39a3ee5e6b4b0d3255bfef95601890afd80709");
	str_check(run_sha1("a"), "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8");
	str_check(run_sha1("abc"), "a9993e364706816aba3e25717850c26c9cd0d89d");
	str_check(run_sha1("message digest"), "c12252ceda8be8994d5fa0290a47231c1d16aae3");
	str_check(run_sha1("abcdefghijklmnopqrstuvwxyz"), "32d10c7b8cf96570ca04ce37f2a19d84240d3a89");
	str_check(run_sha1("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), "761c457bf73b14d27e9e9265c46f4b4dda11f940");
	str_check(run_sha1("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), "50abf5706a150990a08b2c5ea40fa0e585554732");
end:    ;
}

/*
 * SHA224
 */

static const char *run_sha224(const char *str)
{
	return run_hash(str, NULL, digest_SHA224());
}

static void test_sha224(void *ptr)
{
	str_check(run_sha224(""), "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f");
	str_check(run_sha224("a"), "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5");
	str_check(run_sha224("abc"), "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7");
	str_check(run_sha224("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"), "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525");
	str_check(run_sha224("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), "b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e");
end:    ;
}

/*
 * SHA256
 */

static const char *run_sha256(const char *str)
{
	return run_hash(str, NULL, digest_SHA256());
}

static void test_sha256(void *ptr)
{
	str_check(run_sha256(""), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
	str_check(run_sha256("a"), "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb");
	str_check(run_sha256("abc"), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
	str_check(run_sha256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"), "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
	str_check(run_sha256("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), "f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e");
end:    ;
}

/*
 * SHA384
 */

static const char *run_sha384(const char *str)
{
	return run_hash(str, NULL, digest_SHA384());
}

static void test_sha384(void *ptr)
{
	str_check(run_sha384(""), "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b");
	str_check(run_sha384("a"), "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31");
	str_check(run_sha384("abc"), "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7");
	str_check(run_sha384("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"),
		  "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b");
	str_check(run_sha384("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"),
		  "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039");
	str_check(run_sha384("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"),
		  "3d208973ab3508dbbd7e2c2862ba290ad3010e4978c198dc4d8fd014e582823a89e16f9b2a7bbc1ac938e2d199e8bea4");
end:    ;
}

/*
 * SHA512
 */

static const char *run_sha512(const char *str)
{
	return run_hash(str, NULL, digest_SHA512());
}

static void test_sha512(void *ptr)
{
	str_check(run_sha512(""), "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
	str_check(run_sha512("a"), "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75");
	str_check(run_sha512("abc"), "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f");
	str_check(run_sha512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"),
		  "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445");
	str_check(run_sha512("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"),
		  "930d0cefcb30ff1133b6898121f1cf3d27578afcafe8677c5257cf069911f75d8f5831b56ebfda67b278e66dff8b84fe2b2870f742a580d8edb41987232850c9");
	str_check(run_sha512("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"),
		  "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909");
end:    ;
}

/*
 * SHA3-224
 */

static const char *run_sha3_224(const char *hex)
{
	return run_hash(NULL, hex, digest_SHA3_224());
}

static void test_sha3_224(void *ptr)
{
	/* KeccakCodePackage: ShortMsgKAT_SHA3-224.txt */
	stri_check(run_sha3_224(""), "6B4E03423667DBB73B6E15454F0EB1ABD4597F9A1B078E3F5B5A6BC7");
	stri_check(run_sha3_224("CC"), "DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39");
	stri_check(run_sha3_224("41FB"), "BFF295861DAEDF33E70519B1E2BCB4C2E9FE3364D789BC3B17301C15");
	stri_check(run_sha3_224("1F877C"), "14889DF49C076A9AF2F4BCB16339BCC45A24EBF9CE4DCDCE7EC17217");
	stri_check(run_sha3_224("C1ECFDFC"), "A33C58DF8A8026F0F9591966BD6D00EED3B1E829580AB9BE268CAF39");
	stri_check(run_sha3_224("21F134AC57"), "10E580A32199596169331AD43CFCF10264F81565037040028A06B458");
	stri_check(run_sha3_224("C6F50BB74E29"), "FE52C30C95C1E5193207E97D355FDE09453482708C0876AA961508F0");
	stri_check(run_sha3_224("119713CC83EEEF"), "8B449849CB7C4776C593DE58FD5C2E322CB5316BE08A75057A01ED6A");
	stri_check(run_sha3_224("4A4F202484512526"), "01386CDD70589B3B34941EFE16B85071E9BA948179922044F640868E");
	/* 1088 */
	stri_check(run_sha3_224("B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04"),
		   "64D78817714FE05272D3805E6E19056B1649036CDCD5094FD1CC890A");
	/* 1096 */
	stri_check(run_sha3_224("04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D"),
		   "2C4E7C537D0E2AF2261A669BC24BD0DF16D2C72A7F98D7A5EF6A8150");
	/* 2040 */
	stri_check(run_sha3_224("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"),
		   "94689EA9F347DDA8DD798A858605868743C6BD03A6A65C6085D52BED");
end:    ;
}

/*
 * SHA3-256
 */

static const char *run_sha3_256(const char *hex)
{
	return run_hash(NULL, hex, digest_SHA3_256());
}

static void test_sha3_256(void *ptr)
{
	/* KeccakCodePackage: ShortMsgKAT_SHA3-256.txt */
	stri_check(run_sha3_256(""), "A7FFC6F8BF1ED76651C14756A061D662F580FF4DE43B49FA82D80A4B80F8434A");
	stri_check(run_sha3_256("CC"), "677035391CD3701293D385F037BA32796252BB7CE180B00B582DD9B20AAAD7F0");
	stri_check(run_sha3_256("41FB"), "39F31B6E653DFCD9CAED2602FD87F61B6254F581312FB6EEEC4D7148FA2E72AA");
	stri_check(run_sha3_256("1F877C"), "BC22345E4BD3F792A341CF18AC0789F1C9C966712A501B19D1B6632CCD408EC5");
	stri_check(run_sha3_256("C1ECFDFC"), "C5859BE82560CC8789133F7C834A6EE628E351E504E601E8059A0667FF62C124");
	stri_check(run_sha3_256("21F134AC57"), "55BD9224AF4EED0D121149E37FF4D7DD5BE24BD9FBE56E0171E87DB7A6F4E06D");
	stri_check(run_sha3_256("C6F50BB74E29"), "AE0CBC757D4AB088E172ABFD8746289950F92D38A25295658DBF744B5635AF04");
	stri_check(run_sha3_256("119713CC83EEEF"), "E340C9A44373EFCC212F3CB66A047AC34C87FF1C58C4A14B16A2BFC34698BB1D");
	stri_check(run_sha3_256("4A4F202484512526"), "BA4FB009D57A5CEB85FC64D54E5C55A55854B41CC47AD15294BC41F32165DFBA");
	/* 1088 */
	stri_check(run_sha3_256("B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04"),
		   "DF673F4105379FF6B755EEAB20CEB0DC77B5286364FE16C59CC8A907AFF07732");
	/* 1096 */
	stri_check(run_sha3_256("04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D"),
		   "D52432CF3B6B4B949AA848E058DCD62D735E0177279222E7AC0AF8504762FAA0");
	/* 2040 */
	stri_check(run_sha3_256("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"),
		   "C11F3522A8FB7B3532D80B6D40023A92B489ADDAD93BF5D64B23F35E9663521C");
end:    ;
}

/*
 * SHA3-384
 */

static const char *run_sha3_384(const char *hex)
{
	return run_hash(NULL, hex, digest_SHA3_384());
}

static void test_sha3_384(void *ptr)
{
	/* KeccakCodePackage: ShortMsgKAT_SHA3-384.txt */
	stri_check(run_sha3_384(""), "0C63A75B845E4F7D01107D852E4C2485C51A50AAAA94FC61995E71BBEE983A2AC3713831264ADB47FB6BD1E058D5F004");
	stri_check(run_sha3_384("CC"), "5EE7F374973CD4BB3DC41E3081346798497FF6E36CB9352281DFE07D07FC530CA9AD8EF7AAD56EF5D41BE83D5E543807");
	stri_check(run_sha3_384("41FB"), "1DD81609DCC290EFFD7AC0A95D4A20821580E56BD50DBD843920650BE7A80A1719577DA337CFDF86E51C764CAA2E10BD");
	stri_check(run_sha3_384("1F877C"), "14F6F486FB98ED46A4A198040DA8079E79E448DAACEBE905FB4CF0DF86EF2A7151F62FE095BF8516EB0677FE607734E2");
	stri_check(run_sha3_384("C1ECFDFC"), "D92BBD604BDD24B9889508F8558B13E96595AC90BC8A441DAF9B51D6ABC14FFD0835FB9366E3912504264CE87E421CB8");
	stri_check(run_sha3_384("21F134AC57"), "E248D6FF342D35A30EC230BA51CDB161025D6F1C251ACA6AE3531F0682C164A1FC0725B1BEFF808A200C131557A22809");
	stri_check(run_sha3_384("C6F50BB74E29"), "D6DD2ED08C1F644857A15DAFAF80538BEE597278C9ABE047BFBABFB8B1FCB7543E80AE9F7143D00F4DAAF39B138AB3FF");
	stri_check(run_sha3_384("119713CC83EEEF"), "49CA1EB8D71D1FDC7A72DAA320C8F9CA543671C2CB8FE9B2638A8416DF50A790A50D0BB6B88741D7816D6061F46AEA89");
	stri_check(run_sha3_384("4A4F202484512526"), "89DBF4C39B8FB46FDF0A6926CEC0355A4BDBF9C6A446E140B7C8BD08FF6F489F205DAF8EFFE160F437F67491EF897C23");
	/* 1088 */
	stri_check(run_sha3_384("B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04"),
		   "CAD2D28FBDCC3A5D71FB3ADCEEC52313AD41D4FF1F915CAA34EE127839DBF2E9A7B06E1C4ECD6255926C16C06E51EFD0");
	/* 1096 */
	stri_check(run_sha3_384("04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D"),
		   "5B192EBAB47215A8E9FB8E4D561B220B1DC36707A3F085F7BB0175335C393251E3467F945570420C743365D0F09B9E09");
	/* 2040 */
	stri_check(run_sha3_384("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"),
		   "128DC611762BE9B135B3739484CFAADCA7481D68514F3DFD6F5D78BB1863AE68130835CDC7061A7ED964B32F1DB75EE1");
end:    ;
}

/*
 * SHA3-512
 */

static const char *run_sha3_512(const char *hex)
{
	return run_hash(NULL, hex, digest_SHA3_512());
}

static void test_sha3_512(void *ptr)
{
	/* KeccakCodePackage: ShortMsgKAT_SHA3-512.txt */
	stri_check(run_sha3_512(""), "A69F73CCA23A9AC5C8B567DC185A756E97C982164FE25859E0D1DCC1475C80A615B2123AF1F5F94C11E3E9402C3AC558F500199D95B6D3E301758586281DCD26");
	stri_check(run_sha3_512("CC"), "3939FCC8B57B63612542DA31A834E5DCC36E2EE0F652AC72E02624FA2E5ADEECC7DD6BB3580224B4D6138706FC6E80597B528051230B00621CC2B22999EAA205");
	stri_check(run_sha3_512("41FB"), "AA092865A40694D91754DBC767B5202C546E226877147A95CB8B4C8F8709FE8CD6905256B089DA37896EA5CA19D2CD9AB94C7192FC39F7CD4D598975A3013C69");
	stri_check(run_sha3_512("1F877C"), "CB20DCF54955F8091111688BECCEF48C1A2F0D0608C3A575163751F002DB30F40F2F671834B22D208591CFAF1F5ECFE43C49863A53B3225BDFD7C6591BA7658B");
	stri_check(run_sha3_512("C1ECFDFC"), "D4B4BDFEF56B821D36F4F70AB0D231B8D0C9134638FD54C46309D14FADA92A2840186EED5415AD7CF3969BDFBF2DAF8CCA76ABFE549BE6578C6F4143617A4F1A");
	stri_check(run_sha3_512("21F134AC57"), "584219A84E8796076BF1178B14B9D1E2F96A4B4EF11F10CC516FBE1A29639D6BA74FB92815F9E3C5192ED4DCA20AEA5B109D52237C9956401FD44B221F82AB37");
	stri_check(run_sha3_512("C6F50BB74E29"), "4345B92A2AB7EADB6A24EE1D175AC258CCF2F694AC09EC9D47399E4D96F61F30B322C5438C51BACD0D597D00471A41ED8E9C9F146BBC807E6BC385F850FBABFE");
	stri_check(run_sha3_512("119713CC83EEEF"), "50081C93BF73ECC54A5FFE43FC14F8BAEEDBE7DA0302AC984C9E668389886BD064BAB26DDCB616EB4E0E726042B19F3FD50BDD0D2C5B34892E00E6F399DE254F");
	stri_check(run_sha3_512("4A4F202484512526"), "150D787D6EB49670C2A4CCD17E6CCE7A04C1FE30FCE03D1EF2501752D92AE04CB345FD42E51038C83B2B4F8FD438D1B4B55CC588C6B913132F1A658FB122CB52");
	/* 1088 */
	stri_check(run_sha3_512("B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04"),
		   "2E293765022D48996CE8EFF0BE54E87EFB94A14C72DE5ACD10D0EB5ECE029CADFA3BA17A40B2FFA2163991B17786E51CABA79E5E0FFD34CF085E2A098BE8BACB");
	/* 1096 */
	stri_check(run_sha3_512("04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D"),
		   "BE8E14B6757FFE53C9B75F6DDE9A7B6C40474041DE83D4A60645A826D7AF1ABE1EEFCB7B74B62CA6A514E5F2697D585BFECECE12931BBE1D4ED7EBF7B0BE660E");
	/* 2040 */
	stri_check(run_sha3_512("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"),
		   "6E8B8BD195BDD560689AF2348BDC74AB7CD05ED8B9A57711E9BE71E9726FDA4591FEE12205EDACAF82FFBBAF16DFF9E702A708862080166C2FF6BA379BC7FFC2");
end:    ;
}

/*
 * SHAKE128
 */

static const char *run_variable(const char *hex, const struct DigestInfo *mdinfo)
{
	struct DigestContext *ctx;
	uint8_t res[512];
	int len;
	unsigned reslen;
	uint8_t *buf = NULL;

	len = strlen(hex) / 2;
	buf = fromhex(hex, len);

	ctx = digest_new(mdinfo, NULL);
	if (!ctx)
		return "NOMEM";
	digest_update(ctx, buf, len);
	free(buf);

	reslen = 0;
	while (reslen < sizeof(res)) {
		digest_final(ctx, res + reslen);
		reslen += digest_result_len(ctx);
	}
	digest_free(ctx);

	return mkhex(res, reslen);
}

static const char *run_shake128(const char *hex)
{
	return run_variable(hex, digest_SHAKE128());
}

static void test_shake128(void *ptr)
{
	/* KeccakCodePackage: ShortMsgKAT_SHAKE128.txt */
	stri_check(run_shake128(""),
		   "7F9C2BA4E88F827D616045507605853ED73B8093F6EFBC88EB1A6EACFA66EF263CB1EEA988004B93103CFB0AEEFD2A686E01FA4A58E8A3639CA8A1E3F9AE57E235B8CC873C23DC62B8D260169AFA2F75AB916A58D974918835D25E6A435085B2BADFD6DFAAC359A5EFBB7BCC4B59D538DF9A04302E10C8BC1CBF1A0B3A5120EA17CDA7CFAD765F5623474D368CCCA8AF0007CD9F5E4C849F167A580B14AABDEFAEE7EEF47CB0FCA9767BE1FDA69419DFB927E9DF07348B196691ABAEB580B32DEF58538B8D23F87732EA63B02B4FA0F4873360E2841928CD60DD4CEE8CC0D4C922A96188D032675C8AC850933C7AFF1533B94C834ADBB69C6115BAD4692D8619F90B0CDF8A7B9C264029AC185B70B83F2801F2F4B3F70C593EA3AEEB613A7F1B1DE33FD75081F592305F2E4526EDC09631B10958F464D889F31BA010250FDA7F1368EC2967FC84EF2AE9AFF268E0B1700AFFC6820B523A3D917135F2DFF2EE06BFE72B3124721D4A26C04E53A75E30E73A7A9C4A95D91C55D495E9F51DD0B5E9D83C6D5E8CE803AA62B8D654DB53D09B8DCFF273CDFEB573FAD8BCD45578BEC2E770D01EFDE86E721A3F7C6CCE275DABE6E2143F1AF18DA7EFDDC4C7B70B5E345DB93CC936BEA323491CCB38A388F546A9FF00DD4E1300B9B2153D2041D205B443E41B45A653F2A5C4492C1ADD544512DDA2529833462B71A41A45BE97290B6F");
	stri_check(run_shake128("CC"),
		   "4DD4B0004A7D9E613A0F488B4846F804015F0F8CCDBA5F7C16810BBC5A1C6FB254EFC81969C5EB49E682BABAE02238A31FD2708E418D7B754E21E4B75B65E7D39B5B42D739066E7C63595DAF26C3A6A2F7001EE636C7CB2A6C69B1EC7314A21FF24833EAB61258327517B684928C7444380A6EACD60A6E9400DA37A61050E4CD1FBDD05DDE0901EA2F3F67567F7C9BF7AA53590F29C94CB4226E77C68E1600E4765BEA40B3644B4D1E93EDA6FB0380377C12D5BB9DF4728099E88B55D820C7F827034D809E756831A334C078FC28ACB76B5ADB3BFF6DD659CAA49CC34F726880F293BD3FC132027AE7602242064EFEC4D9D656E069D4DFAE0B40C1D6B6CDB21D89D00E168B0B74D72EBB3B672B57AF3E99C85DA2F41CE70672CD0E0521678FC56EAB6314A0B3AF8B724376C01433D84943A73AF703D293634BC24322992756EE261FFF0D71BFFB8AEBF1026A6A345F2EAED505BC7E02498A3225FC91499DD5F5E30E386557C5FE0A88BC2337C80D7EA42B60622960230577CE800CB63594F619B7DE31E026429B7648C5835AFC00559FA4C7695D6DD9F7B2537A265E9AF7A2C986F8B60E7DC6EB3C4D805A6EEFB6FBB5BFDE21ED7E41CFDBEB02B0BAB76F9998BA1E52815A246B084EFAE7960AFFC2BA5C647E7CC05EF8120568432DFDE1D7246473304808985600A1AFC20B99185AF25E89DC2EC6F4880DC79BAD50DFFCC9EA");
	stri_check(run_shake128("41FB"),
		   "09C9652BB968996A35E4814E27587131F53FD01AB9FE83758ACEB8134FCECA24C84F592CEE43A4476E8853FCAB7DAFEF7B60ECFEBFD70DFCF587B3AF358A286FE3713BF4735A84975BB65E3586C81EA716BFB999626DC973A495A6E0024061387D628E9E59DFD2B39C68C8CEAD665AB43F6D2625A10630761DFB60276EA97B280442462246C6D74A1960A8419A76A37B68449A9E427D6A7EC1FBDF4760847AD6F6F5A08CEFB767CAEB6C2382F4F3D0E49DE4428CD4240635C9136911A82FF0B9C74569A1B7C8AF72AB1EA5F2F6F6A45E3BB08229ADDFA916B18A74F7939C5130152AC8343A10694154FDC6E1570EC7ECABBB01EDDC92EF0BB1B3DB914C74CCE399ACC9B766FD7494B2EF27AC57B80D52535942D55E2DBFAA61CDF3F48759AA612DED11421855AD15FFAB91462A56F873BBAF4FE88457A47B6C0594818D0A9189895239C1429ED8754EEE5498F4D0FB6C9D0DF0EB5316289E72C6AAEB8C61317B409156D4221CE6CFC7C5F39272D87C2D884F88F1B8B3C05CA9E235ED92C7DD7806CDADA7166CC1B9107DA5E6536D4FF111BF9199D6B72AC17D874323D68D76AEC4650F1A4B067C50215362201A7F71116BF6633AF08D712804B83F08A5DC7CCD4315963106D50453D44EFF59C9C652F4A924BE93C0B958EA286B0A4B597899A28C9BD5419C042668AA7B0CFCAC4CDF9260F2824ABF3EE79FEF53EBE3C36DF831");
	stri_check(run_shake128("C1ECFDFC"),
		   "B5EB987E7CBFC7C9D140AFD20B500E30F2F71188BCE885951F22FBC35DE40E7411311BC8479F895A069600483751295F5C3855474D65436B347608583125A6BD41CA30DC99CB342B72A96F8A2213E98A16BBB9E38A141B4FBA68C1A723D1D578A91B4A1BAFD03B25BD3CFB6D4E1E044637889C6D7AF0209DBB5E10837D5C5991D2766012E7E86A82838B455B96D882B7E25C9072A66DA22B0ACB992FD3CCF5FBC28625B896BDF8D4B7358901D12698FD823FE09AFB4F238631EE1277752F2900E38C7FC163381A01601DBDE8CD30A9A8EE8353DEF6C610B50962D0EC1F4F3EEC2AFD7FCD5296E299C23005960C1AABF3408EDE96DE18E10FDE99774B5BD33092639D8BE9D872130C96311EB6DCA82CC28A62C01E3F9C454251D87FA890284C06187F904CF23CCC62690628C590BC8B84EEE68371D3412DCB5648A634F2359951CD011028E61C28A33EC4E31550C1C0E4123D1091E22D56BD587E73B7DD43C1B431D9547398C5E7EB6C682EA2FD758C86C1222C0D6AB236B8106DAC19338E86CB8F6EC0FB607D70C938CAC172C80079B018BB62939546505B8B6E146A3AD7A35F20D7F9A353F1E6535C23AE93CE8F78C045402E707A664F5918E6836D343240E6112EFA29DFC4D18778C91E2B8530E4FF6E4947623D31DAFAC9B0D194E36C44E10B1A846935684F89ADB44782E8FFF2E3E62B65D18140F10D36CD817F835EB0C0");
	/* 1024 */
	stri_check(run_shake128("2B6DB7CED8665EBE9DEB080295218426BDAA7C6DA9ADD2088932CDFFBAA1C14129BCCDD70F369EFB149285858D2B1D155D14DE2FDB680A8B027284055182A0CAE275234CC9C92863C1B4AB66F304CF0621CD54565F5BFF461D3B461BD40DF28198E3732501B4860EADD503D26D6E69338F4E0456E9E9BAF3D827AE685FB1D817"),
		   "5E0693E64DBE678055A31D80DA85A81A1BA5EB1CB6394B7B1AA2ADC2B8C1FEE4F41DFF3623502FCC349D4B2BEBBC1311EF291EF47F490C6FC1323075B519C87DBD77CB94C213EB548278A6C11B0C321C941FBEB9742B690A9777875D8A1FD0627B5CE3679A6DBCC8B53D775E73AC9F951749FC353EEE7220CAB40F4F7C2EEA311CD0B50974AF188EA9742509C4121FC07A70FC28F0633B56BABB451554BDF86396594B57B1F88DFA07DD4F8C47AB5FFBAD03DA1DA642EF4A6E75A952E8ACB3989136B8A792E6F33B24DA2CC4E422192EEBAF121561BD33A6D79DF6517B3F001E674F39BB98D9AA0E15226389B17655E7B2796D75EDE0430A0E7D07039B8E35642570F6457F8344A59DBDD5128F74F9AB83CD152DB17987019AA0B27B6E7951CEF67B574D05535CEB5EB023A39B5731B6AFE6CBB8BFEE0F71819D791C288BFE9EBE6706778FE62A80AEF22CB9C0920938502F30A20FB4A4822D0C0D42D50D67FA6A76EF919C985C6306FEA73A3BF41006F0FE60C0D92B80106D8C715B97E35E39FA8ECA59220B86C2C46B9C61EEBCE07DA4B64CAD247A67112F6548AA693BFC4BFC9AE78144571CBE7B81B83C8832938A71FD3AC6B8E4AECF17FD2848FD9558DC5DDB261CCAAF083AFFA341EA295C7264F35734F5179038AB70C90E03BF53E3FF438A9C0655EC38F5B9DAB7A4C3BA852A2A0593C80999818B460015C79093B1D6");
	/* 2040 */
	stri_check(run_shake128("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"),
		   "14236E75B9784DF4F57935F945356CBE383FE513ED30286F91060759BCB0EF4BAAC858ECAE7C6E7EDD498F01A082B63FA57D22540231E2E25C83EFB3B3F2953A5F674502AB635226446B84937643DCD5789EE73F1D734BC8FE5F7F0883AB10961B9A31FF60DEE16159BC6982EFB08545984BF71FED1C4CD81C0914B4C19FCFEEF54AF4BBE372F18CFCD3A18657F5B9450F99A78F0FA2C3CDCA7461C4ED7569536883B66CD87E9C200962902EAA16A54DB6A0A5CC26D889038C0760810B5BB4F33F1E5D639B6F9BC7CA62BA6F8C9F8DE770260AFE47F4E0F82F102198EBA27F543252AC8DDD83E1B8DB0A91AC65633FD12A550EBE96F93AA6704ED5905C234FA6D9203910CBD02DE166C4C3348FB81EF7B84AE1455FE318B5FD170883F49BA2F24289C479A2C7531406BA989BEAEF3A79F659028642E9B033F7DEB9ECEC3A7A9F1DBD2451FCB47C81E21E91D20B924C6BD04C1F0B2710D2E570CD24BAD5B5DE4E49AA80B6ADD5507B4D2E510370C7AFA814D7E1A7E278E53D7CCF49A0A866CA3A7B5BB71EF3425E460FEEB29149F217066613695F85506A0946CF68979F04AE073AF8028976BF0C5BDC2212E8C364583DE9FBD03B34DDEE5EC4CFA8ED8CE592971D0108FAF76C8940E25E6C5F865584C34A233C14F00532673FDBE388CC7E98A5B867B1C591307A9015112B567FF6B4F318114111FC95E5BD7C9C60B74C1F8725");
end:    ;
}

/*
 * SHAKE256
 */

static const char *run_shake256(const char *hex)
{
	return run_variable(hex, digest_SHAKE256());
}

static void test_shake256(void *ptr)
{
	/* KeccakCodePackage: ShortMsgKAT_SHAKE256.txt */
	stri_check(run_shake256(""),
		   "46B9DD2B0BA88D13233B3FEB743EEB243FCD52EA62B81B82B50C27646ED5762FD75DC4DDD8C0F200CB05019D67B592F6FC821C49479AB48640292EACB3B7C4BE141E96616FB13957692CC7EDD0B45AE3DC07223C8E92937BEF84BC0EAB862853349EC75546F58FB7C2775C38462C5010D846C185C15111E595522A6BCD16CF86F3D122109E3B1FDD943B6AEC468A2D621A7C06C6A957C62B54DAFC3BE87567D677231395F6147293B68CEAB7A9E0C58D864E8EFDE4E1B9A46CBE854713672F5CAAAE314ED9083DAB4B099F8E300F01B8650F1F4B1D8FCF3F3CB53FB8E9EB2EA203BDC970F50AE55428A91F7F53AC266B28419C3778A15FD248D339EDE785FB7F5A1AAA96D313EACC890936C173CDCD0FAB882C45755FEB3AED96D477FF96390BF9A66D1368B208E21F7C10D04A3DBD4E360633E5DB4B602601C14CEA737DB3DCF722632CC77851CBDDE2AAF0A33A07B373445DF490CC8FC1E4160FF118378F11F0477DE055A81A9EDA57A4A2CFB0C83929D310912F729EC6CFA36C6AC6A75837143045D791CC85EFF5B21932F23861BCF23A52B5DA67EAF7BAAE0F5FB1369DB78F3AC45F8C4AC5671D85735CDDDB09D2B1E34A1FC066FF4A162CB263D6541274AE2FCC865F618ABE27C124CD8B074CCD516301B91875824D09958F341EF274BDAB0BAE316339894304E35877B0C28A9B1FD166C796B9CC258A064A8F57E27F2A");
	stri_check(run_shake256("CC"),
		   "DDBF55DBF65977E3E2A3674D33E479F78163D592666BC576FEB5E4C404EA5E5329C3A416BE758687DE1A55E23D9E48A7D3F3CE6D8F0B2006A935800ECA9C9FC903D86F065367221067658B4D7473ED54800D196FBE1089811DD9B47F21E3698B1573653ADAD231C39F145B586D6C0133378416138E4423F7AF7DACFFE965706A3C49024002B53BA05871E3F066694B163630B0053BE41FA45D4317EAA84214254038A37A9C83D62EB9EA6E6ACAFA6BBFE5AF9F389690D5A9E27BF97C1B93D93ECF36DF6DA212E12B2448154156E94687336B6DA91E368512B9F34C616166542923F3618640D930F922A3DDDD2F87920378541446F2223F593931BD1BA02E2907CE3404621F26B900D05A1119A9E4934A7CD818DD9237445BF50475A011EA0078788801D21DFECB7A2BB294E4956DFA71D8CC472405342BF80120FE18A551D88D6ABC24D83F077BFB25EBDE5F4B03678D677EE646DCE3496D5138BE108782CA5A00AAFF3CB4BB873EC0F2E932DD74847033EC5F07254E3027B0AC12DB7D6D3F90B53D8A5BD63B99C3BF5CD38B453D7CB12D0AE2BF1CFD3EE88AF71BB6606B0B791DEFC2D762C8641BB8319FE17321EBA407EB744699D92B35ABD79F5B9A85408C93D61233FECE638023875AA02B9EDBACC84003A28CCA2D55A0742D635FDA888905986CA01C1E6420D49EC25224D84E915DFD1638A492282F1FD053770168953C");
	stri_check(run_shake256("41FB"),
		   "B64ECACD5F7499ACC085C908D35DCC1FC0131816F28D360592E1265079F92A5F844C4BF6AA50D98D52720797E8C992F43C76A73FD95F9BC4CD272157842ADA2518190FCA342DC20D0C57CDDF01B3DDF77977EDED63445E40BE82DF8D26DB629A2D307EE9FE28D2FE557E3971858C6D67C42BE2CF44DD7570521CE06474467425B7AAAE39DB90945BAD388009ED5715C684BB4E4981EEA324ECF66584AD08D9F27C6A4DCF615591857BC7364E8A7C136661AE5FFE828C734DD5EA5A071276E8477B8525E02B7B445D91CC6E37D58740DC2B069BE6D92E7DF95C1AB52B76F7761AE34328962EAC7156E460B3C04FFECAEC8722A56E7373285E42D4CAC5498F8D7DD5ECDA9F9973A32F8D425171E1390BFC812C9EE4D4AB8FA9A0D93AA90A4C258FC64D77BBCF49977E87C3810C80C4585168996A31F446F9391A193B888CD321E22E9368F4F11495FE124141C04015532345D7CB0A13A4DD9007D737B3A176A88E5FC153D4AC2E8CD641C40C4261BBA70E1B87114030FF67CB22ACEC90AC288D6B59D25B00038468B4780254FAC4EF158EC2CD52C0AB9217EED1ED0A5E7B4C4B3A64B1824E2B27AA53398765D5352BD1ED0E9C7B3FB264D141741659F7D8FD0EEEC9F9163C42AFDB540D5F2C87833880A0C942AE4CCEA7FFF2F4C798B8AAF24C33BE8054A09459A3AF7200D555334241709A18ECF88CE93C99234D6AB0285916AE");
	stri_check(run_shake256("C1ECFDFC"),
		   "CE7FBC15503986E3B84530D84A16EF64332A6EA57E354E9F2054BFC2AA8891F94B4FDD42FD227C94B33F9AD45CF3982239F81FBC2CBAC7809F01E1C294073AD6D2821C1283C5C427F41FD46455EF7B4EA0D6D5E249AF95FAC80A8678C1A5318443E63122DDFED2591FC690527F97A09920941B02439AF3BA36B17FD588E187FCBC1FF109AB4A9ECFC671926EF0CC9C82EE06EC6759E2758A8859B8FA9DDF46D6C049621FF589F3FF56C9419D6F48A68B68FEFD068ABEC24824D7FC150277C2439BF78D15D59DBAA2CB17E5282E6E9ED744841F4A4BBB778CFEAB0471CE850B2A2F948DB3926F733EF7B3AA9AB8EA84278DCA62B0691F5DD13EA11660A8E2FB5CD8BA74A352C044E7219E750F29B62F94ED268A68029B94B64F3D4F2193A7FC69ED34A59657471F04C4961084EBB581ABCC9D50E3674A7DEBB285FC7215BF6606DB1392A7F1619B347A4D07D230DA07811FDE2AEBE45A70178CF841C619F712EF26BEEECC4183A1040076E73FCF6FABE5C25A4B71D564A97CF3C88F1856D9C8B42E94F746CE4605D2AAEB56D1DA5527598E17E5680D309788E09910BEB74DF7D3C3CD4EC680083F5845F4A1C15070E57979C01B89DF7BE64435EA4817BC0AD23ACCA6CB878F7131F02F9215E2D5C12CF3B4D24B29620C891A54AC8BE6E3DEC08397887DE0EA86B81A5459B968FBAE18B4B0324DE7E7C1AEEFC7598644CE1FF8F9");
	/* 1024 */
	stri_check(run_shake256("2B6DB7CED8665EBE9DEB080295218426BDAA7C6DA9ADD2088932CDFFBAA1C14129BCCDD70F369EFB149285858D2B1D155D14DE2FDB680A8B027284055182A0CAE275234CC9C92863C1B4AB66F304CF0621CD54565F5BFF461D3B461BD40DF28198E3732501B4860EADD503D26D6E69338F4E0456E9E9BAF3D827AE685FB1D817"),
		   "E38785ED93686FA741FBB6E5BE64933963C3C872F7A4E8C8D540EC3F82284605625D32A24BCE8B40264EB51B164DD86F318ACFD9867F3BF23998275042AAF23BDA01F602622448957B81E51475F15CDB31A9297EE390F681E460EC01A024F183110C728BB09A12DDE89E6F5DE2B4F8C17D981E3E9C531E6D4C19448A4A6BE28853AFA2FBA216F7C8E2CE4E4DE31F6B0E129CB5DA118B4A59569A439C1095EB50C51CD83105B1A12B3F7086947EA7381969A78308F8FDDAC8B2D87F4540A8E7CAC8932CA76201F86561ADD09D833361851CEB9759AD1EEED4E00ED19C642BC6D0AED034276B66D818E8791A7C1F42F8604E8B026D4635E3BDB27CA0FB28E7517BF662BB99AE3A3C698AD0AA2F02408E76A5F93ABFC933B1691A89EE3EBCA2885EA633D0FEA4DBCD03B05B68E0F4D267144FDC0898DE46011832ADC872F4A7F0D8933CDD482CA54FFA81774BA083D3D9FE07DE94F8E03FF66D9F1BFE7504E8A497591BA8F52758F92E0A4CA6A93979CD1F55EE9DBA17BAC6C69E83DDED4CE2DBFFB0B48C0C6AED657405DE18E7891B3C854127459E89FE5442CA6D5C223B046147BFDEE435CF4EFAEFD705373DC87E20B7D31C7D37907D30B8B32054BB9ECA80D0AFABB5EC5053D94D517F158EA958C7458CF71FCA85B607A352A9A3B8F4F161E3B8853F6BB61864BAD12DD9B57ECDA507A3CAA63A1DEC726B518CB4FDDEED6A34");
	/* 2040 */
	stri_check(run_shake256("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"),
		   "8A5199B4A7E133E264A86202720655894D48CFF344A928CF8347F48379CEF347DFC5BCFFAB99B27B1F89AA2735E23D30088FFA03B9EDB02B9635470AB9F1038985D55F9CA774572DD006470EA65145469609F9FA0831BF1FFD842DC24ACADE27BD9816E3B5BF2876CB112232A0EB4475F1DFF9F5C713D9FFD4CCB89AE5607FE35731DF06317949EEF646E9591CF3BE53ADD6B7DD2B6096E2B3FB06E662EC8B2D77422DAAD9463CD155204ACDBD38E319613F39F99B6DFB35CA9365160066DB19835888C2241FF9A731A4ACBB5663727AAC34A401247FBAA7499E7D5EE5B69D31025E63D04C35C798BCA1262D5673A9CF0930B5AD89BD485599DC184528DA4790F088EBD170B635D9581632D2FF90DB79665CED430089AF13C9F21F6D443A818064F17AEC9E9C5457001FA8DC6AFBADBE3138F388D89D0E6F22F66671255B210754ED63D81DCE75CE8F189B534E6D6B3539AA51E837C42DF9DF59C71E6171CD4902FE1BDC73FB1775B5C754A1ED4EA7F3105FC543EE0418DAD256F3F6118EA77114A16C15355B42877A1DB2A7DF0E155AE1D8670ABCEC3450F4E2EEC9838F895423EF63D261138BAAF5D9F104CB5A957AEA06C0B9B8C78B0D441796DC0350DDEABB78A33B6F1F9E68EDE3D1805C7B7E2CFD54E0FAD62F0D8CA67A775DC4546AF9096F2EDB221DB42843D65327861282DC946A0BA01A11863AB2D1DFD16E3973D4");
end:    ;
}

/*
 * HMAC
 */

static const char *run_hmac(const char *key, const char *str, const struct DigestInfo *impl)
{
	struct HMAC *ctx;
	uint8_t res[512];
	int len = strlen(str);
	int reslen;

	ctx = hmac_new(impl, key, strlen(key), NULL);
	if (!ctx)
		return "NOMEM";
	reslen = hmac_result_len(ctx);

	hmac_update(ctx, str, len);
	hmac_final(ctx, res);
	hmac_free(ctx);

	return mkhex(res, reslen);
}

static const char *run_hmac_sha1(const char *key, const char *str)
{
	return run_hmac(key, str, digest_SHA1());
}

static void test_hmac(void *ptr)
{
	const char *long_key = (
		"quite a very long key, longer than a sha1 block size, "
		"so it needs to be sha-1d before being used as a key");
	const char *text = "The quick brown fox jumps over the lazy dog";

	str_check(run_hmac_sha1("", ""),
		  "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d");

	str_check(run_hmac_sha1("shrt", ""),
		  "41fee95de96c437cf6c2f38363eb38eb0067ff64");

	str_check(run_hmac_sha1(long_key, ""),
		  "496ca9bda3e523814ba7f99f68a2035e4de7702a");

	str_check(run_hmac_sha1(long_key, text),
		  "924e1ee84da31f5f569a27dd6201533b42c999c6");

	str_check(run_hmac_sha1("key", text),
		  "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9");
end:    ;
}

/*
 * keccak_prng
 */

static void test_keccak_prng(void *z)
{
	struct KeccakPRNG state;
	const char *ent = "The quick brown fox jumps over the lazy dog.";
	const char *ent2 = "More entropy.";
	uint8_t buf[32];
	int i;

	tt_assert(keccak_prng_init(&state, 1) == false);
	tt_assert(keccak_prng_init(&state, 1024) == true);
	tt_assert(keccak_prng_extract(&state, buf, 32) == false);

	keccak_prng_add_data(&state, ent, strlen(ent));

	tt_assert(keccak_prng_extract(&state, buf, 32) == true);
	str_check(mkhex(buf, 32), "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded638788");

	if (1) {
		tt_assert(keccak_prng_extract(&state, buf, 32) == true);
	} else {
		for (i = 0; i < 32; i++) {
			tt_assert(keccak_prng_extract(&state, buf + i, 1) == true);
		}
	}
	str_check(mkhex(buf, 32), "9064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760");

	tt_assert(keccak_prng_extract(&state, buf, 32) == true);
	str_check(mkhex(buf, 32), "7ca34f68abb61bbd1821c0a499599426031a56c495b3cf91b84cacafb9be816b");

	tt_assert(keccak_prng_extract(&state, buf, 32) == true);
	str_check(mkhex(buf, 32), "e7afb50b3a1c80f654ba212be0ad8a4be8f6a476bfcc66b9401fe65924bd547d");

	keccak_prng_add_data(&state, ent2, strlen(ent2));

	tt_assert(keccak_prng_extract(&state, buf, 32) == true);
	str_check(mkhex(buf, 32), "ec9f73358469f4b7fea10dfb7dfaa768f573089b8e00507ec3a1fdfb2e60b35d");
end:    ;
}

/*
 * chacha.
 */

static const char *run_chacha(const char *key, const char *iv, uint32_t c1, uint32_t c2)
{
	int klen = strlen(key) / 2;
	void *kb, *ivb;
	struct ChaCha ctx;
	uint8_t output[128];

	if (klen != 32 && klen != 16)
		return "KeyError";
	if (strlen(iv) != 8*2)
		return "IvError";

	kb = fromhex(key, klen);
	if (klen == 32) {
		chacha_set_key_256(&ctx, kb);
	} else {
		chacha_set_key_128(&ctx, kb);
	}
	free(kb);

	ivb = fromhex(iv, 8);
	chacha_set_nonce(&ctx, c1, c2, ivb);
	free(ivb);

	if (1) {
		static unsigned int blkver = 0;
		static const int blklens[] = {128, 1, 7, 11, 66};
		int blk = blklens[blkver++];
		int n, need = sizeof(output);
		uint8_t *dst = output;
		if (blkver >= ARRAY_NELEM(blklens)) blkver = 0;
		while (need > 0) {
			n = (need > blk) ? blk : need;
			chacha_keystream(&ctx, dst, n);
			dst += n;
			need -= n;
		}
	} else {
		chacha_keystream(&ctx, output, sizeof(output));
	}

	return mkhex(output, sizeof(output));
}

/* https://tools.ietf.org/html/draft-strombergson-chacha-test-vectors-01 */
static void test_chacha(void *z)
{
	/* TC1: All zero key and IV. */
	str_check(run_chacha("00000000000000000000000000000000", "0000000000000000", 0, 0),
		  "89670952608364fd00b2f90936f031c8e756e15dba04b8493d00429259b20f46cc04f111246b6c2ce066be3bfb32d9aa0fddfbc12123d4b9e44f34dca05a103f"
		  "6cd135c2878c832b5896b134f6142a9d4d8d0d8f1026d20a0a81512cbce6e9758a7143d021978022a384141a80cea3062f41f67a752e66ad3411984c787e30ad");
	str_check(run_chacha("0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000", 0, 0),
		  "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"
		  "9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f");

	/* TC2: Single bit in key set. All zero IV. */
	str_check(run_chacha("01000000000000000000000000000000", "0000000000000000", 0, 0),
		  "ae56060d04f5b597897ff2af1388dbceff5a2a4920335dc17a3cb1b1b10fbe70ece8f4864d8c7cdf0076453a8291c7dbeb3aa9c9d10e8ca36be4449376ed7c42"
		  "fc3d471c34a36fbbf616bc0a0e7c523030d944f43ec3e78dd6a12466547cb4f7b3cebd0a5005e762e562d1375b7ac44593a991b85d1a60fba2035dfaa2a642d5");
	str_check(run_chacha("0100000000000000000000000000000000000000000000000000000000000000", "0000000000000000", 0, 0),
		  "c5d30a7ce1ec119378c84f487d775a8542f13ece238a9455e8229e888de85bbd29eb63d0a17a5b999b52da22be4023eb07620a54f6fa6ad8737b71eb0464dac0"
		  "10f656e6d1fd55053e50c4875c9930a33f6d0263bd14dfd6ab8c70521c19338b2308b95cf8d0bb7d202d2102780ea3528f1cb48560f76b20f382b942500fceac");

	/* TC3: Single bit in IV set. All zero key. */
	str_check(run_chacha("00000000000000000000000000000000", "0100000000000000", 0, 0),
		  "1663879eb3f2c9949e2388caa343d361bb132771245ae6d027ca9cb010dc1fa7178dc41f8278bc1f64b3f12769a24097f40d63a86366bdb36ac08abe60c07fe8"
		  "b057375c89144408cc744624f69f7f4ccbd93366c92fc4dfcada65f1b959d8c64dfc50de711fb46416c2553cc60f21bbfd006491cb17888b4fb3521c4fdd8745");
	str_check(run_chacha("0000000000000000000000000000000000000000000000000000000000000000", "0100000000000000", 0, 0),
		  "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"
		  "5305e5e44aff19b235936144675efbe4409eb7e8e5f1430f5f5836aeb49bb5328b017c4b9dc11f8a03863fa803dc71d5726b2b6b31aa32708afe5af1d6b69058");

	/* TC4: All bits in key and IV are set. */
	str_check(run_chacha("ffffffffffffffffffffffffffffffff", "ffffffffffffffff", 0, 0),
		  "992947c3966126a0e660a3e95db048de091fb9e0185b1e41e41015bb7ee50150399e4760b262f9d53f26d8dd19e56f5c506ae0c3619fa67fb0c408106d0203ee"
		  "40ea3cfa61fa32a2fda8d1238a2135d9d4178775240f99007064a6a7f0c731b67c227c52ef796b6bed9f9059ba0614bcf6dd6e38917f3b150e576375be50ed67");
	str_check(run_chacha("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffff", 0, 0),
		  "d9bf3f6bce6ed0b54254557767fb57443dd4778911b606055c39cc25e674b8363feabc57fde54f790c52c8ae43240b79d49042b777bfd6cb80e931270b7f50eb"
		  "5bac2acd86a836c5dc98c116c1217ec31d3a63a9451319f097f3b4d6dab0778719477d24d24b403a12241d7cca064f790f1d51ccaff6b1667d4bbca1958c4306");

	/* TC5: Every even bit set in key and IV. */
	str_check(run_chacha("55555555555555555555555555555555", "5555555555555555", 0, 0),
		  "357d7d94f966778f5815a2051dcb04133b26b0ead9f57dd09927837bc3067e4b6bf299ad81f7f50c8da83c7810bfc17bb6f4813ab6c326957045fd3fd5e19915"
		  "ec744a6b9bf8cbdcb36d8b6a5499c68a08ef7be6cc1e93f2f5bcd2cad4e47c18a3e5d94b5666382c6d130d822dd56aacb0f8195278e7b292495f09868ddf12cc");
	str_check(run_chacha("5555555555555555555555555555555555555555555555555555555555555555", "5555555555555555", 0, 0),
		  "bea9411aa453c5434a5ae8c92862f564396855a9ea6e22d6d3b50ae1b3663311a4a3606c671d605ce16c3aece8e61ea145c59775017bee2fa6f88afc758069f7"
		  "e0b8f676e644216f4d2a3422d7fa36c6c4931aca950e9da42788e6d0b6d1cd838ef652e97b145b14871eae6c6804c7004db5ac2fce4c68c726d004b10fcaba86");

	/* TC6: Every odd bit set in key and IV. */
	str_check(run_chacha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa", 0, 0),
		  "fc79acbd58526103862776aab20f3b7d8d3149b2fab65766299316b6e5b16684de5de548c1b7d083efd9e3052319e0c6254141da04a6586df800f64d46b01c87"
		  "1f05bc67e07628ebe6f6865a2177e0b66a558aa7cc1e8ff1a98d27f7071f8335efce4537bb0ef7b573b32f32765f29007da53bba62e7a44d006f41eb28fe15d6");
	str_check(run_chacha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa", 0, 0),
		  "9aa2a9f656efde5aa7591c5fed4b35aea2895dec7cb4543b9e9f21f5e7bcbcf3c43c748a970888f8248393a09d43e0b7e164bc4d0b0fb240a2d72115c4808906"
		  "72184489440545d021d97ef6b693dfe5b2c132d47e6f041c9063651f96b623e62a11999a23b6f7c461b2153026ad5e866a2e597ed07b8401dec63a0934c6b2a9");

	/* TC7: Sequence patterns in key and IV. */
	str_check(run_chacha("00112233445566778899aabbccddeeff", "0f1e2d3c4b5a6978", 0, 0),
		  "d1abf630467eb4f67f1cfb47cd626aae8afedbbe4ff8fc5fe9cfae307e74ed451f1404425ad2b54569d5f18148939971abb8fafc88ce4ac7fe1c3d1f7a1eb7ca"
		  "e76ca87b61a9713541497760dd9ae059350cad0dcedfaa80a883119a1a6f987fd1ce91fd8ee0828034b411200a9745a285554475d12afc04887fef3516d12a2c");
	str_check(run_chacha("00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100", "0f1e2d3c4b5a6978", 0, 0),
		  "9fadf409c00811d00431d67efbd88fba59218d5d6708b1d685863fabbb0e961eea480fd6fb532bfd494b2151015057423ab60a63fe4f55f7a212e2167ccab931"
		  "fbfd29cf7bc1d279eddf25dd316bb8843d6edee0bd1ef121d12fa17cbc2c574cccab5e275167b08bd686f8a09df87ec3ffb35361b94ebfa13fec0e4889d18da5");

	/* TC8: Random Key */
	str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb735", "1ada31d5cf688221", 0, 0),
		  "826abdd84460e2e9349f0ef4af5b179b426e4b2d109a9c5bb44000ae51bea90a496beeef62a76850ff3f0402c4ddc99f6db07f151c1c0dfac2e56565d6289625"
		  "5b23132e7b469c7bfb88fa95d44ca5ae3e45e848a4108e98bad7a9eb15512784a6a9e6e591dce674120acaf9040ff50ff3ac30ccfb5e14204f5e4268b90a8804");
	str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d", "1ada31d5cf688221", 0, 0),
		  "f63a89b75c2271f9368816542ba52f06ed49241792302b00b5e8f80ae9a473afc25b218f519af0fdd406362e8d69de7f54c604a6e00f353f110f771bdca8ab92"
		  "e5fbc34e60a1d9a9db17345b0a402736853bf910b060bdf1f897b6290f01d138ae2c4c90225ba9ea14d518f55929dea098ca7a6ccfe61227053c84e49a4a3332");

	/* Counter overflow */
	str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb735", "1ada31d5cf688221", 0xFFFFFFFF, 0),
		  "9220e40c9fa71e1e210b216b5f1c829e73d31c6891447084d5c5bc29db812ec2f4c64a594214a3437cb04548f9a1c4839f03405f1e2b5db69d9df11474a4610c"
		  "60f15b0e01460b61d8ac73cac1de084c741c157fb75d52719ed98a62bcada8187041035178a1845b164f0d82b4fc20a6ad1668d3177fac688cb08d3df75281fe");
	str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d", "1ada31d5cf688221", 0xFFFFFFFF, 0xFFFFFFFF),
		  "489b569d1a0649a3185f04dfda7cbb688503f3485ea0754e7a9c17452e0f6a123a1e24d4313d79c9bf7c4fd714211dca39c1717f29b7d137158b7f620bdcb759"
		  "f63a89b75c2271f9368816542ba52f06ed49241792302b00b5e8f80ae9a473afc25b218f519af0fdd406362e8d69de7f54c604a6e00f353f110f771bdca8ab92");
end:    ;
}

/*
 * csrandom.
 */

static uint32_t calc_lim(uint32_t val)
{
	uint32_t mod, lim;

	/* mod = 2**32 % x = (2**32 - x) % x */
	mod = -val % val;

	/* wait for value in range [0 .. 2**32 - mod ) */
	lim = -mod;

	return lim;
}

static void test_csrandom(void *z)
{
	uint32_t half, v, lim, i, v2;

	half = 1 << 31;
	for (v = 2; v < 1024; v++) {
		lim = calc_lim(v);
		int_check(lim % v, 0);
		tt_assert(lim >= v || lim == 0);
	}

	for (v = half - 1024; v < half + 1024; v++) {
		lim = calc_lim(v);
		int_check(lim % v, 0);
		tt_assert(lim >= v || lim == 0);
	}

	for (v = 0xFFFFFF00U; v != 0; v++) {
		lim = calc_lim(v);
		int_check(lim % v, 0);
		tt_assert(lim >= v);
	}

	lim = 0;
	v = csrandom();
	for (i = 0; i < 100; i++) {
		v2 = csrandom();
		if (v == v2)
			lim++;
		v = v2;
	}
	tt_assert(lim < 10);
end:    ;
}

/*
 * Launcher.
 */

struct testcase_t crypto_tests[] = {
	{ "md5", test_md5 },
	{ "sha1", test_sha1 },
	{ "sha224", test_sha224 },
	{ "sha256", test_sha256 },
	{ "sha384", test_sha384 },
	{ "sha512", test_sha512 },
	{ "sha3-224", test_sha3_224 },
	{ "ska3-256", test_sha3_256 },
	{ "sha3-384", test_sha3_384 },
	{ "sha3-512", test_sha3_512 },
	{ "shake128", test_shake128 },
	{ "shake256", test_shake256 },
	{ "hmac", test_hmac },
	{ "keccak_prng", test_keccak_prng },
	{ "chacha", test_chacha },
	{ "csrandom", test_csrandom },
	END_OF_TESTCASES
};
